package com.zenglx.multilanguage.interceptor;

import cn.hutool.core.util.ClassUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ParameterUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import com.zenglx.multilanguage.domain.BaseDomain;
import com.zenglx.multilanguage.domain.Language;
import com.zenglx.multilanguage.exception.MapperException;
import com.zenglx.multilanguage.metadata.ZlxTableInfo;
import com.zenglx.multilanguage.metadata.ZlxTableInfoHelper;
import com.zenglx.multilanguage.util.LanguageHelper;
import com.zenglx.multilanguage.util.StringUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 分页拦截器
 * <p>
 * 默认对 left join 进行优化,虽然能优化count,但是加上分页的话如果1对多本身结果条数就是不正确的
 *
 * @author liangxi.zeng
 * @since 3.4.0
 */
@NoArgsConstructor
@Slf4j
public class ZlxMultiLanguageInterceptor extends PaginationInnerInterceptor {

    private static final String COLUMN_LANG = "lang";

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            //非多语言分页查询
            ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableByMapper(getMapperClassName(ms.getId()));
            if (tableInfo != null && !tableInfo.isMultiLanguage()) {
                //非多语言
                return super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            } else {
                // 多语言
                IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
                if (page == null || page.getSize() < 0 || !page.searchCount()) {
                    //普通查询
                    PluginUtils.mpBoundSql(boundSql).sql(autoMultiWhereSql(boundSql.getSql(),ms));
                    return true;
                }
                BoundSql countSql;
                MappedStatement countMs = buildCountMappedStatement(ms, page.countId());
                if (countMs != null) {
                    countSql = countMs.getBoundSql(parameter);
                } else {
                    countMs = buildAutoCountMappedStatement(ms);
                    String countSqlStr = autoCountSql(page.optimizeCountSql(), boundSql.getSql(),ms);
                    PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
                    countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
                    PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
                }

                CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
                List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);
                long total = 0;
                if (CollectionUtils.isNotEmpty(result)) {
                    // 个别数据库 count 没数据不会返回 0
                    Object o = result.get(0);
                    if (o != null) {
                        total = Long.parseLong(o.toString());
                    }
                }
                page.setTotal(total);
                return continuePage(page);
            }
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        //非多语言分页查询
        ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableByMapper(getMapperClassName(ms.getId()));
        if (tableInfo != null && !tableInfo.isMultiLanguage()) {
            //非多语言
            super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        } else {
            IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
            if (null == page) {
                return;
            }

            // 处理 orderBy 拼接
            boolean addOrdered = false;
            String buildSql = boundSql.getSql();
            List<OrderItem> orders = page.orders();
            if (CollectionUtils.isNotEmpty(orders)) {
                addOrdered = true;
                buildSql = this.concatOrderBy(buildSql, orders);
            }

            // size 小于 0 且不限制返回值则不构造分页sql
            Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;
            if (page.getSize() < 0 && null == _limit) {
                if (addOrdered) {
                    PluginUtils.mpBoundSql(boundSql).sql(buildSql);
                }
                return;
            }

            handlerLimit(page, _limit);
            IDialect dialect = findIDialect(executor);

            final Configuration configuration = ms.getConfiguration();
            DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());
            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);

            List<ParameterMapping> mappings = mpBoundSql.parameterMappings();
            Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();
            model.consumers(mappings, configuration, additionalParameter);
            mpBoundSql.sql(model.getDialectSql());
            mpBoundSql.parameterMappings(mappings);
            //多语言
            buildSql = autoMultiWhereSql(boundSql.getSql(),ms);
            PluginUtils.mpBoundSql(boundSql).sql(buildSql);

        }


    }


    /**
     * 获取自动优化的 countSql
     *
     * @param sql              sql
     * @return countSql
     */
    protected String autoMultiWhereSql(String sql, MappedStatement ms) {
        try {
            Select select = (Select) CCJSqlParserUtil.parse(sql);
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
            // 包含 join 连表,进行判断是否移除 join 连表
            if (optimizeJoin) {
                List<Join> joins = plainSelect.getJoins();
                if (CollectionUtils.isNotEmpty(joins)) {
                    boolean canRemoveJoin = true;
                    Expression expression = plainSelect.getWhere();
                    if(expression==null) {
                        return select.toString();
                    }
                    if(expression instanceof EqualsTo) {
                        return select.toString();
                    }
                    Expression whereExp = ((Parenthesis) expression).getExpression();
                    ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableByMapper(getMapperClassName(ms.getId()));
                    List<String> multiLanguageColumns = tableInfo.getMultiLanguageColumns()
                            .stream().map(TableFieldInfo::getColumn).collect(Collectors.toList());
                    setWhereSql(whereExp,multiLanguageColumns);
                    String whereS = Optional.ofNullable(whereExp).map(Expression::toString).orElse(StringPool.EMPTY);
                    // 不区分大小写
                    whereS = whereS.toLowerCase();
                    for (Join join : joins) {
                        if (!join.isLeft()) {
                            canRemoveJoin = false;
                            break;
                        }
                        FromItem rightItem = join.getRightItem();
                        String str = "";
                        if (rightItem instanceof Table) {
                            Table table = (Table) rightItem;
                            str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;
                        } else if (rightItem instanceof SubSelect) {
                            SubSelect subSelect = (SubSelect) rightItem;
                            /* 如果 left join 是子查询，并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */
                            if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {
                                canRemoveJoin = false;
                                break;
                            }
                            str = subSelect.getAlias().getName() + StringPool.DOT;
                        }
                        // 不区分大小写
                        str = str.toLowerCase();
                        String onExpressionS = join.getOnExpression().toString();
                        /* 如果 join 里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */
                        if (onExpressionS.contains(StringPool.QUESTION_MARK) || whereS.contains(str)) {
                            canRemoveJoin = false;
                            break;
                        }
                    }
                    if (canRemoveJoin) {
                        plainSelect.setJoins(null);
                    }
                }
            }
            // 优化 SQL
            return select.toString();
        } catch (JSQLParserException e) {
            // 无法优化使用原 SQL
            log.warn("optimize this sql to a count sql has exception, sql:\"" + sql + "\", exception:\n" ,e);
        } catch (Exception e) {
            log.error("optimize this sql to a count sql has error, sql:\"" + sql , e);
        }
        return lowLevelCountSql(sql);
    }

    /**
     * 判断是否执行 {@link Executor#update(MappedStatement, Object)}
     * <p>
     * 如果不执行update操作,则影响行数的值为 -1
     *
     * @param executor  Executor(可能是代理对象)
     * @param ms        MappedStatement
     * @param parameter parameter
     */
    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
            ms.getKeyGenerator()
                    .processBefore(executor, ms, null, parameter);
        }
        return true;
    }



    /**
     * 获取自动优化的 countSql
     *
     * @param optimizeCountSql 是否进行优化
     * @param sql              sql
     * @return countSql
     */
    protected String autoCountSql(boolean optimizeCountSql, String sql, MappedStatement ms) {
        if (!optimizeCountSql) {
            return lowLevelCountSql(sql);
        }
        try {
            Select select = (Select) CCJSqlParserUtil.parse(sql);
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
            Distinct distinct = plainSelect.getDistinct();
            GroupByElement groupBy = plainSelect.getGroupBy();
            List<OrderByElement> orderBy = plainSelect.getOrderByElements();

            if (CollectionUtils.isNotEmpty(orderBy)) {
                boolean canClean = true;
                if (groupBy != null) {
                    // 包含groupBy 不去除orderBy
                    canClean = false;
                }
                if (canClean) {
                    for (OrderByElement order : orderBy) {
                        // order by 里带参数,不去除order by
                        Expression expression = order.getExpression();
                        if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {
                            canClean = false;
                            break;
                        }
                    }
                }
                if (canClean) {
                    plainSelect.setOrderByElements(null);
                }
            }
            //#95 Github, selectItems contains #{} ${}, which will be translated to ?, and it may be in a function: power(#{myInt},2)
            for (SelectItem item : plainSelect.getSelectItems()) {
                if (item.toString().contains(StringPool.QUESTION_MARK)) {
                    return lowLevelCountSql(select.toString());
                }
            }
            // 包含 distinct、groupBy不优化
            if (distinct != null || null != groupBy) {
                return lowLevelCountSql(select.toString());
            }
            // 包含 join 连表,进行判断是否移除 join 连表
            if (optimizeJoin) {
                List<Join> joins = plainSelect.getJoins();
                if (CollectionUtils.isNotEmpty(joins)) {
                    boolean canRemoveJoin = true;
                    Expression expression = plainSelect.getWhere();
                    if(expression==null) {
                        return lowLevelCountSql(select.toString());
                    }
                    Expression whereExp = ((Parenthesis) expression).getExpression();
                    Class clazz = ms.getResultMaps().get(0).getType();
                    ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableInfo(clazz);
                    List<String> multiLanguageColumns = tableInfo.multiLanguageColumns();
                    setWhereSql(whereExp,multiLanguageColumns);
                    String whereS = Optional.ofNullable(whereExp).map(Expression::toString).orElse(StringPool.EMPTY);
                    // 不区分大小写
                    whereS = whereS.toLowerCase();
                    for (Join join : joins) {
                        if (!join.isLeft()) {
                            canRemoveJoin = false;
                            break;
                        }
                        FromItem rightItem = join.getRightItem();
                        String str = "";
                        if (rightItem instanceof Table) {
                            Table table = (Table) rightItem;
                            str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;
                        } else if (rightItem instanceof SubSelect) {
                            SubSelect subSelect = (SubSelect) rightItem;
                            /* 如果 left join 是子查询，并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */
                            if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {
                                canRemoveJoin = false;
                                break;
                            }
                            str = subSelect.getAlias().getName() + StringPool.DOT;
                        }
                        // 不区分大小写
                        str = str.toLowerCase();
                        String onExpressionS = join.getOnExpression().toString();
                        /* 如果 join 里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */
                        if (onExpressionS.contains(StringPool.QUESTION_MARK) || whereS.contains(str)) {
                            canRemoveJoin = false;
                            break;
                        }
                    }
                    if (canRemoveJoin) {
                        plainSelect.setJoins(null);
                    }
                }
            }
            // 优化 SQL
            plainSelect.setSelectItems(COUNT_SELECT_ITEM);
            return select.toString();
        } catch (JSQLParserException e) {
            // 无法优化使用原 SQL
            log.warn("optimize this sql to a count sql has exception, sql:\"" + sql + "\", exception:\n" + e.getCause());
        } catch (Exception e) {
            log.error("optimize this sql to a count sql has error, sql:\"" + sql , e);
        }
        return lowLevelCountSql(sql);
    }


    private void setWhereSql(Expression whereExp,List<String> multiLanguageColumns) {
        if(whereExp instanceof Parenthesis) {
            Parenthesis parenthesis = (Parenthesis) whereExp;
            Expression expression = parenthesis.getExpression();
            setWhereSql(expression,multiLanguageColumns);
        }
        if (whereExp instanceof OrExpression) {
            OrExpression orExpression = (OrExpression) whereExp;
            Expression left = orExpression.getLeftExpression();
            Expression right = orExpression.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if (whereExp instanceof AndExpression) {
            AndExpression andExpression = (AndExpression) whereExp;
            Expression left = andExpression.getLeftExpression();
            Expression right = andExpression.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if (whereExp instanceof InExpression) {
            InExpression inExpression = (InExpression) whereExp;
            Expression left = inExpression.getLeftExpression();
            Expression right = inExpression.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if(whereExp instanceof LikeExpression){
            LikeExpression likeExpression = (LikeExpression) whereExp;
            Expression left = likeExpression.getLeftExpression();
            Expression right = likeExpression.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if(whereExp instanceof EqualsTo) {
            EqualsTo equalsTo = (EqualsTo) whereExp;
            Expression left = equalsTo.getLeftExpression();
            Expression right = equalsTo.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if(whereExp instanceof NotEqualsTo) {
            NotEqualsTo notEqualsTo = (NotEqualsTo) whereExp;
            Expression left = notEqualsTo.getLeftExpression();
            Expression right = notEqualsTo.getRightExpression();
            setWhereSql(left,multiLanguageColumns);
            setWhereSql(right,multiLanguageColumns);
        } else if(whereExp instanceof Column) {
            Column column = (Column) whereExp;
            if (multiLanguageColumns.contains(column.getColumnName())) {
                column.setColumnName("t."+column.getColumnName());
            } else {
                column.setColumnName("b."+column.getColumnName());
            }
        }
    }

    /**
     * 获取mapper的类名
     *
     * @param msId msId
     * @return String
     */
    private String getMapperClassName(String msId) {
        if (!msId.contains(".")) {
            throw new MapperException("当前MappedStatement的id=" + msId + ",不符合MappedStatement的规则!");
        }
        return msId.substring(0, msId.lastIndexOf('.'));
    }

    /**
     * {@link Executor#update(MappedStatement, Object)} 操作前置处理
     * <p>
     * 改改sql啥的
     *
     * @param executor  Executor(可能是代理对象)
     * @param statement        MappedStatement
     * @param parameter parameter
     */
    @Override
    public void beforeUpdate(Executor executor, MappedStatement statement, Object parameter) throws SQLException {
        ZlxTableInfo tableInfo = ZlxTableInfoHelper.getTableByMapper(getMapperClassName(statement.getId()));
        if (tableInfo != null && tableInfo.isMultiLanguage()) {
            Connection connection = executor.getTransaction().getConnection();
            switch (statement.getSqlCommandType()) {
                case INSERT:
                    insertMultiLanguage(tableInfo, parameter, connection);
                    break;
                case UPDATE:
                    List<String> columnNameList = tableInfo.getEntityClassColumns().stream().map(TableFieldInfo::getColumn)
                                    .collect(Collectors.toList());
                    updateMultiLanguage(tableInfo, parameter, connection, new HashSet<>(columnNameList));
                    break;
                case DELETE:
                    proceedDeleteMultiLanguage(tableInfo, parameter, connection);
                    break;
                default:
                    break;
            }
        }
    }


    private void proceedDeleteMultiLanguage(ZlxTableInfo table, Object object, Connection connection) throws SQLException {
        List<Object> objs = new ArrayList<>();
        List<String> keys = new ArrayList<>();
        for (TableFieldInfo tableFieldInfo : table.getEntityClassPkColumns()) {
            String column = tableFieldInfo.getColumn();
            keys.add(column + " = ?");
            if (ClassUtil.isBasicType(object.getClass())) {
                // deleteByPrimaryKey 的情况
                objs.add(object);
            } else {
                try {
                    objs.add(tableFieldInfo.getField().get(object));
                } catch (IllegalAccessException e) {
                    throw new MapperException(e);
                }
            }

        }
        if (!keys.isEmpty()) {
            executeSql(connection, "DELETE FROM "
                    + table.getTableName()+"_tl"
                    + " WHERE " + StringUtil.join(keys, " AND "), objs);
        }
    }

    private void insertMultiLanguage(ZlxTableInfo table, Object object, Connection connection) throws SQLException{
        Map<String, Object> columnMap = new HashMap<>(table.getMultiLanguageColumns().size() + table.getEntityClassPkColumns().size() + 1);
        Map<String, Map<String, String>> fieldLangValueMap = getTls(object);

        String placeholders = buildPlaceholders(table.getMultiLanguageColumns().size() + table.getEntityClassPkColumns().size() + 1);
        String sql = "INSERT INTO " + table.getTableName()+"_tl" + "(";

        for (TableFieldInfo tableFieldInfo : table.getEntityClassPkColumns()) {
            try {
                columnMap.put(tableFieldInfo.getColumn(), tableFieldInfo.getField().get(object));
            } catch (IllegalAccessException e) {
                throw new MapperException(e);
            }
        }
        for (Language language : getLanguages()) {
            StringBuilder sb = new StringBuilder(sql);
            columnMap.remove(COLUMN_LANG);
            columnMap.put(COLUMN_LANG, language.getCode());
            for (TableFieldInfo tableFieldInfo : table.getMultiLanguageColumns()) {
                columnMap.remove(tableFieldInfo.getColumn());
                columnMap.put(tableFieldInfo.getColumn(), getValue(fieldLangValueMap, tableFieldInfo.getField().getName(), language.getCode(), () -> {
                    try {
                        return tableFieldInfo.getField().get(object);
                    } catch (IllegalAccessException e) {
                        throw new MapperException(e);
                    }
                }));
            }
            sb.append(StringUtil.join(columnMap.keySet(), ","));
            sb.append(") VALUES (").append(placeholders).append(")");
            executeSql(connection, sb.toString(), columnMap.values());
        }
    }

    private void insertMultiLanguage(ZlxTableInfo table, String lang, Object parameter, Connection connection, Map<String, Map<String, String>> fieldLangValueMap) throws SQLException {
        StringBuilder builder = new StringBuilder();
        List<Object> values = new ArrayList<>();
        List<Object> keys = new ArrayList<>();
        List<String> placeholders = new ArrayList<>();
        for (TableFieldInfo tableFieldInfo : table.getMultiLanguageColumns()) {
            try {
                values.add(tableFieldInfo.getField().get(parameter));
            } catch (IllegalAccessException e) {
                throw new MapperException(e);
            }
            keys.add(tableFieldInfo.getColumn());
            placeholders.add("?");
        }
        for (TableFieldInfo tableFieldInfo : table.getMultiLanguageColumns()) {
            values.add(getValue(fieldLangValueMap, tableFieldInfo.getField().getName(), lang, () -> {
                try {
                    return tableFieldInfo.getField().get(parameter);
                } catch (IllegalAccessException e) {
                    throw new MapperException(e);
                }
            }));
            keys.add(tableFieldInfo.getColumn());
            placeholders.add("?");
        }
        keys.add("lang");
        values.add(lang);
        placeholders.add("?");

        builder.append("INSERT INTO ").append(table.getTableName()+"_tl").append(" (");
        builder.append(StringUtil.join(keys, ","));
        builder.append(") VALUES (");
        builder.append(StringUtil.join(placeholders, ","));
        builder.append(")");
        log.debug("Insert missing multi language record: {} ,parameters: {}", builder, values);
        executeSql(connection, builder.toString(), values);
    }

    private void updateMultiLanguage(ZlxTableInfo table, Object object, Connection connection, Set<String> updateColumnSet) throws SQLException {
        Map<String, Object> whereColumnMap = new LinkedHashMap<>(table.getEntityClassPkColumns().size() + 1);
        Map<String, Object> setColumnMap = new LinkedHashMap<>(table.getMultiLanguageColumns().size() + table.getEntityClassPkColumns().size() + 1);
        Map<String, Map<String, String>> fieldLangValueMap = getTls(object);
        Map<String, Set<String>> updateLanguageSet = getUpdateLanguage(fieldLangValueMap);
        String sql = "UPDATE " + table.getTableName()+"_tl" + " SET ";

        for (TableFieldInfo tableFieldInfo : table.getEntityClassPkColumns()) {
            if(object instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) object;
                object = paramMap.get("param1");
            }
            Object value = null;
            try {
                value = tableFieldInfo.getField().get(object);
            } catch (IllegalAccessException e) {
                throw new MapperException(e);
            }
            if (value == null) {
                return;
            }
            whereColumnMap.put(tableFieldInfo.getColumn() + " = ?", value);
        }
        for (Language language : getLanguages()) {
            setColumnMap.clear();
            whereColumnMap.remove(COLUMN_LANG + " = ?");
            whereColumnMap.put(COLUMN_LANG + " = ?", language.getCode());
            for (TableFieldInfo tableFieldInfo : table.getMultiLanguageColumns()) {
                if (updateColumnSet.contains(tableFieldInfo.getColumn())
                        && ((updateLanguageSet.containsKey(tableFieldInfo.getProperty()) && updateLanguageSet.get(tableFieldInfo.getProperty()).contains(language.getCode()))
                        || Objects.equals(LanguageHelper.language(), language.getCode()))) {
                    Object finalObject = object;
                    setColumnMap.put(tableFieldInfo.getColumn() + " = ? ", getValue(fieldLangValueMap, tableFieldInfo.getField().getName(), language.getCode(), () -> {
                        try {
                            return tableFieldInfo.getField().get(finalObject);
                        } catch (IllegalAccessException e) {
                            throw new MapperException(e);
                        }
                    }));
                }
            }
            if (org.springframework.util.CollectionUtils.isEmpty(setColumnMap)) {
                continue;
            }
            String placeholderSql = sql +
                    StringUtil.join(setColumnMap.keySet(), ",") +
                    " WHERE " +
                    StringUtil.join(whereColumnMap.keySet(), " AND ");
            setColumnMap.putAll(whereColumnMap);
            int updateCount = executeSql(connection, placeholderSql, setColumnMap.values());
            if (updateCount < 1) {
                log.warn("Update multi language failed. update count: {}", updateCount);
                // 讲道理，下面这个不会执行
                insertMultiLanguage(table, language.getCode(), object, connection, fieldLangValueMap);
            }
        }
    }

    private Map<String, Set<String>> getUpdateLanguage(Map<String, Map<String, String>> fieldLangValueMap) {
        Map<String, Set<String>> fieldUpdateLanguageSet = new HashMap<>();
        if (!org.springframework.util.CollectionUtils.isEmpty(fieldLangValueMap)) {
            fieldLangValueMap.forEach((field, langValue) -> fieldUpdateLanguageSet.put(field, langValue.keySet()));
        }
        return fieldUpdateLanguageSet;
    }


    private int executeSql(Connection connection, String sql, Collection<Object> params) throws SQLException {
        log.debug("==>  Preparing: {}", sql);
        log.debug("==> Parameters: {}", params);
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            int i = 1;
            for (Object obj : params) {
                ps.setObject(i++, obj);
            }
            ps.execute();
            int updateCount = ps.getUpdateCount();
            log.debug("<==      Total: {}", updateCount);
            return updateCount;
        }
    }

    private List<Language> getLanguages() {
        return LanguageHelper.languages();
    }

    private Object getValue(Map<String, Map<String, String>> fieldLangValueMap, String field, String lang, DefaultValue defaultValue) {
        if (fieldLangValueMap != null && fieldLangValueMap.containsKey(field)) {
            Map<String, String> fieldValue = fieldLangValueMap.get(field);
            // 此处判断 StringUtils.hasLength(fieldValue.get(lang)) 是因为Oracle如果字段非空写入""会报错
            if (fieldValue != null && fieldValue.containsKey(lang) && org.springframework.util.StringUtils.hasLength(fieldValue.get(lang))) {
                return fieldValue.get(lang);
            }
        }
        return defaultValue.getDefaultValue();
    }

    private interface DefaultValue {
        /**
         * 获取默认值
         *
         * @return 默认值
         */
        Object getDefaultValue();
    }

    private String buildPlaceholders(int placeholderCnt) {
        StringBuilder sb = new StringBuilder("?");
        while ((--placeholderCnt) > 0) {
            sb.append(", ?");
        }
        return sb.toString();
    }

    private Map<String, Map<String, String>> getTls(Object object) {
        Map<String, Map<String, String>> fieldLangValueMap = null;
        if (object instanceof BaseDomain) {
            fieldLangValueMap = ((BaseDomain) object).get_tls();
        }
        return fieldLangValueMap;
    }

}
